Resource Editors
Volume Number: 2
Issue Number: 5
Column Tag: Resource Roundup
All About Resource Editors
By Joel West, MacTutor Contributing Editor
Choosing and Using a Resource Editor
This article is the first of several articles on using resources when developing a
Macintosh application. This month, we’ll look at the structure of resources and how the
two resource editors available from Apple can be used to free you from RMaker.
If you’re just getting started, you can also use the resource editors to understand
how a typical application is put together. The study of actual applications can provide a
valuable education in Macintosh development, much as an MBA student studies the case
histories of existing businesses.
The article will describe the general structure of resources, and how to choose
between the various resource-building tools available to the developer.
Inescapable Resources
If you’ve gone very far with program development on the Mac, then by now you
know it’s impossible to write a program of any size without a thorough understanding of
resources. Although some might consider them a needless extra step of in program
development, they do make it easier to make certain types of changes in a program --
particularly now, two years after the Mac’s introduction, when there are two complete
Resource Editors available. One of these editors can, in fact, be adapted to edit just
about any sort of data structure.
The proper use of resources also provides a notable benefit over traditional
approaches when dealing with the international marketplace. If all the text seen by a
user is contained in the application’s resources, then your program can be translated to
run in a foreign country -- without making your source code available to someone else.
This article is inteded to provided a brief introduction to resources. The
standard reference on the topic is, of course, Inside Macintosh, under the chapter “The
Resource Manager: A Programmer’s Guide.” From a survey I’ve taken of developers, it
usually takes several readings before most of the details sink in.
Once you understand the general resource concepts, you still don’t know how to
use specific resources. They’re described in the respective chapters descripting the
corresponding Toolbox libraries, such as the Menu Manager, Font Manager, and so on.
The Structure of Resources
One of the most controversial aspect of the Macintosh design is that every file
contains two “forks”, or sections: the data fork and the resource fork. I say
controversial, because most files you’ll find on your Mac normally have only one
component or the other. For example, the MacWrite file that contains this article has
only a data fork; the MacWrite application, the System and the Finder all have only
resources. (Other computers have the same sorts of files, but there’s usually a bit
somewhere that indicates whether the file has program or data.)
One of the few examples I know of with both data and resources is a MacTerminal
document. You can access the data received in a terminal session by editing the data
fork with MacWrite. Or, you can reclaim the terminal’s configuration (‘CNFG’) from
MacTerminal in the resource fork. For most purposes, you can assume that the data
fork contains unformatted ASCII text, which is the most common, but by no means only,
situation.
Resources, however, should not just be considered a fancy name for what you’ll
find in the .EXE file of your IBM PC (sorry) or the a.out of your UNIX system.
Resources are the relational database of Macintosh systems programming: not only do
they provide a way of storing information necessary to run a program, but they also
provide a standard hierarchy for that information.
Every resource has three identifiers that serve to make it unique. The first
idenitifer (or “key”, if you want a database talisman) is, of course, resource file that
contains the resource. Traditional repositories of these are the System file and
individual applications. The Resource Manager does allow combining resources from
several files, as will be discussed later.
The second identifier is a four-character ASCII resource type. Normally, this
consists of four capital letters, as in ‘MENU’, ‘WIND’, and ‘CNTL’ for menus, windows,
and controls, respectively. However, a few resource names end in a space or number
sign, such as ‘STR ’ and ‘STR#’, which refer to strings and a list of strings.
Third, each resource within a given type and file must have a unique 16-bit
integer, referred to as the resource ID. In most cases, resources that you assign should
be numbered between 128 and 32,767. Resources in the range 0 to 127 are reserved
for system (Apple-defined) usages; negative id’s are generally used by resources that
are nested within other resources, such as in a desk accessory.
Alternately, each resource of a given type can have a specific name, as for a type
font. Although the resource id must always be unique, a unique resource name can be
used as the third key. Usually the id or name is used to reference a given type, but not
both.
In most cases, the layout of all resources of a given type will follow the same
explicit set of rules, no matter what file or id they have. Unfortunately, there are a
few types that do not, such as ‘PREC’ and ‘INTL’.
The overall structure of the resource fork of a file is represented by Figure 1.
Each resource fork contains both the actual resource data, and the resource map, which
is nothing more than a index to where each resource is located. As suggested by the
illustration, each resource of a given type and id (or name) can be found by tracing the
links from the beginning of the resource map. The links are represented as an offset
within the resource map (2 bytes) or within the resource data (3 bytes), depending on
where the referenced data lies.
Not shown are the resources which reference other resources, usually by
resource ID and an implied resource type. For example, alert and dialog template
resources (used by the dialog manager routines) will normally reference a
corresponding item list. Item lists can, in turn, reference control, icons, or
QuickDraw pictures. And if you want a non-standard control, menu, or window, you
would reference a ‘CDEF’, ‘MDEF’ or ‘WDEF’ resource from within a ‘CNTL’, ‘MENU’ or
’WIND’ resource.
The custom is that related resources (e.g., dialogs and item lists) are numbered
the same, but this is only a convention, not a requirement of the resource manager. It
does, however, make it easier for you (or a foreign marketeer) to modify national or
personal preferences.
Fig. 1 Resource Overview
Accessing Resources Within a Program
Normally, a particular resource will be referenced through the appropriate
manager routine, e.g., the Menu Manager, Window Manager, or Control Manager. Most
such routines only require the resource ID, since they already know the resource type
to look for when searching the open resource files of your application.
However, you can always get at an arbitrary resource on a particular file as
follows (all examples are given for Megamax C):
resfilno = OpenResFile("System");
if (resfilno >= 0)
reshandle = GetResource(RT_ICON, rsrcid);
else
MyQuit(ResError());
NOTE: If you’re just getting started with resources, or you’ve recently switched
languages, be aware that there are two possible approaches for passing resource type
arguments. For those using assembler, some C’s, and languages that closely follow Lisa
Pascal, the resource type will usually be a 32-bit word with the 4 ASCII characters
stored high-to-low. Such types can be given in hexadecimal, although some compilers
allow you to show it as a character string.
However, most languages define a series of 2 or more characters as a string,
which is a 32-bit pointer to the 4 characters. In Megamax C, for example, any
resource type argument is passed as a string (pointer), and the interface routine
fetches the referenced ASCII word before calling the toolbox.
To tell the two forms apart, look at the menu setup routine of the skeleton
application supplied by the vendor. It will probably contain a statement to merge desk
accessories into the Apple menu that looks like one of the following:
AddResMenu(myMenus[ appleMenu], 'DRVR');
AddResMenu(myMenus[ appleMenu], 0x44525652);
AddResMenu(myMenus[ appleMenu], "DRVR");
The form used by the second argument to AddResMenu is either by value (first
two forms) or by pointer (third form). This is the same form you must use in any
other routine that expects a resource type. To make it easy to switch between different
programming systems, you should use a symbolic definition, such as the corresponding
C declarations:
#define RT_ICON 'ICON'
#define RT_CON 0x49434F4E
#define RT_ICON "ICON
Resource Attributes
We’ve already seen how every resource has:
• Four-letter resource type
• 16-bit integer resource id
• (Optional) resource name, as a Pascal string
Each resource also has a 16-bit word holding resource attributes. However,
only the low-order byte is used; six of these bits are used as boolean flags, as shown by
their symbolic names in Figure 2.
For a new application, you would typically define only three of these attributes.
If resPreload is set, the resource to be read into memory when your program launches;
you would normally want all your menus declared this way, for example. The
resPurgeable flag allows the resource to be purged (removed) any time more memory
is needed, while resLocked pr events the resource from being relocated or purged, thus
avoiding handle-dereferencing memory problems.
You can gain the resource attribute for any resource you have a handle for by a
simple call:
attrs = GetResAttrs(res handle);
Although the resource type remains fixed, you can change the resource ID, name,
or attributes while your program is running. A good example of this would be the
SetResInfo(res handle, ID, name);
SetResAttrs(res handle, attrs)
You should distinguish between the original copy of the resource in the resource
file, and the copy that is currently in memory. Setting (or resetting) most of these
attributes will only have an effect once the changed resource has been written to disk
and then re-read. (The exception is resProtected, which takes effect immediately).
Instead, if you need to protect the copy of the resource in memory, you should use the
customary memory manager routines HLock and HNoPurge.
The one attribute you don’t change directly is resChanged. Instead, you call
ChangedResource and then the resource manager will assure that the modified resource
is written out at some point. To be on the safe side, you may want to force the changes
to be written immediately:
ChangedResource(res handle);
WriteResource(res handle);
UpdateResFile(resfilno);
If you want to change the length of the resource -- such as to lengthen a string
field -- there is no easy way to do it. Instead, you have the create a new copy of the
resource and remove the old copy: